package cn.yhp.chatgpt.interfaces;

import cn.bugstack.chatglm.model.ChatCompletionRequest;
import cn.bugstack.chatglm.model.Model;
import cn.bugstack.chatglm.model.Role;
import cn.bugstack.chatglm.session.Configuration;
import cn.bugstack.chatglm.session.OpenAiSession;
import cn.bugstack.chatglm.session.OpenAiSessionFactory;
import cn.bugstack.chatglm.session.defaults.DefaultOpenAiSessionFactory;
import cn.yhp.chatgpt.application.IWeiXinValidateService;
import cn.yhp.chatgpt.domain.receive.model.MessageTextEntity;
import cn.yhp.chatgpt.infrastructure.util.XmlUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * 微信公众号，消息处理服务
 * @author yhp
 * @description
 * @date 2024/1/8
 */
@RestController
@RequestMapping("/wx/portal/{appid}")
public class WeiXinPortalController {

    private final Logger logger = LoggerFactory.getLogger(WeiXinPortalController.class);

    @Value("${wx.config.originalId}")
    private String originalId;

    @Autowired
    private IWeiXinValidateService weiXinValidateService;

    private final OpenAiSession openAiSession;

    // 存放用户问的每个问题ai回答的结果future，5min过期
    private final Cache<String, ChatGptInfo> openAiFutureMap =
            CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();

    public WeiXinPortalController() {
        Configuration configuration = new Configuration();
        configuration.setApiHost("https://open.bigmodel.cn/");
        // 注册智谱AI，即可获取
        configuration.setApiSecretKey("b2947f2e2495528e1e5af85f4bb3539a.eZys1aEnL6FGHNsU");
        OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration);
        this.openAiSession = factory.openSession();
        logger.info("开启 openAiSession...");
    }

    /**
     * 处理微信服务器发来的get请求，进行签名的验证（在公众号的基本配置/填写服务器下面url配置会验证）
     * 如：http://28d61792.r11.cpolar.top/wx/portal/xx
     *
     * @param appid     微信端AppID
     * @param signature 微信端发来的签名
     * @param timestamp 微信端发来的时间戳
     * @param nonce     微信端发来的随机字符串
     * @param echostr   微信端发来的验证字符串
     * @return
     */
    @GetMapping(produces = "text/plain;charset=utf-8")
    public String validate(@PathVariable String appid, @RequestParam String signature,
                           @RequestParam String timestamp, @RequestParam String nonce, @RequestParam String echostr) {
        try {
            logger.info("微信公众号验签信息[appid]{}开始 [signature:{}, timestamp:{}, nonce:{}, echostr:{}]", appid, signature, timestamp, nonce, echostr);
            if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
                throw new IllegalArgumentException("请求参数非法，请核实!");
            }
            if (weiXinValidateService.checkSign(signature, timestamp, nonce)) {
                return echostr;
            }
            return null;
        } catch (Exception e) {
            logger.error("微信公众号验签失败", e);
            return null;
        }
    }


    // 处理微信客户端转发的消息(AppID 是开发者与微信开放平台进行通信和授权验证的标识；OpenID 是用户在公众号中的唯一标识；OriginalID 是公众号的全局唯一标识符)
    @PostMapping(produces = "application/xml;charset=utf-8")//返回的内容格式
    public String post(@PathVariable String appid,
                       @RequestBody String requestBody,
                       @RequestParam("signature") String signature,
                       @RequestParam("timestamp") String timestamp,
                       @RequestParam("nonce") String nonce,
                       @RequestParam("openid") String openid,
                       @RequestParam(name = "encrypt_type", required = false) String encType,
                       @RequestParam(name = "msg_signature", required = false) String msgSignature) {
        logger.info("接收微信消息：appid:{},消息[xml格式]：{}", appid, requestBody);
        // 一个用户，一个问题缓存
        ChatGptInfo info = openAiFutureMap.getIfPresent(openid);
        String answer = null;
        MessageTextEntity messageTextEntity = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class);
        String content = messageTextEntity.getContent().trim();
        try {
            // 没问过问题，直接调用ai任务，并存入缓存
            if (info == null) {
                info=new ChatGptInfo();
                info.setFuture(doChatGptTask(content));
                info.addRetryTimes();
                openAiFutureMap.put(openid, info);
            }
            // ai回答的答案获取，超过3s,进入异常逻辑
            answer = info.getFuture().get(3, TimeUnit.SECONDS);
            // 回答正常清空该用户问的问题任务
            openAiFutureMap.invalidate(openid);
        } catch (Exception e) {
            logger.error("接收微信公众号信息请求{}失败 {}", openid, requestBody, e);
            answer = "AI思考中，请回复任意消息获取答案";
            if (info.getRetryTimes() >=3){
                answer="AI忙不过来，回复1继续等待，或者直接换个问题";
                if (!"1".equals(content)) {
                    // 问题作废
                    openAiFutureMap.invalidate(openid);
                }
            }
            info.addRetryTimes();
        }
        // 回复消息
        MessageTextEntity textEntity = new MessageTextEntity();
        textEntity.setCreateTime(String.valueOf(System.currentTimeMillis()));
        // 代表哪个公众号
        textEntity.setFromUserName(originalId);
        // 代表发送哪个用户
        textEntity.setToUserName(openid);
        // 文本消息
        textEntity.setMsgType("text");
        textEntity.setContent(answer);
        return XmlUtil.beanToXml(textEntity);
    }

    /**
     * 调用智谱ai回答问题，并将结果放入map中
     *
     * @param content
     */
    private Future<String> doChatGptTask(String content) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        return executorService.submit(() -> {
            ChatCompletionRequest request = new ChatCompletionRequest();
            request.setModel(Model.CHATGLM_TURBO);
            request.setPrompt(new ArrayList<ChatCompletionRequest.Prompt>() {
                {
                    add(ChatCompletionRequest.Prompt.builder()
                            .role(Role.user.getCode())
                            .content(content)
                            .build());
                }
            });
            // 同步获取结果
            try {
                return openAiSession.completions(request).get();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
    }


    @Data
    static class ChatGptInfo{
        /**
         * 用户问题ai任务
         */
        private Future<String> future;

        /**
         * 重试次数
         */
        private int retryTimes;

        public void addRetryTimes(){
            retryTimes++;
        }
    }
}
